package service

import (
	"fmt"
	"go-chromedp-client/structs"
	"go-chromedp-client/util"
	neturl "net/url"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"

	"context"
	"encoding/json"
	"net/http"
	"os"
	"sync"
	"time"
)

// 超时时间
var downTimeOut = 900
var maxProcces = 10

func DownLoad(w http.ResponseWriter, r *http.Request) {
	util.ApiHeader(&w)
	name := r.PostFormValue("name")
	url := r.PostFormValue("url")
	ua := r.PostFormValue("ua")
	end_url := r.PostFormValue("end_url")
	tags := r.PostFormValue("tags")
	subpage := r.PostFormValue("subpage")
	//fmt.Println(url, end_url)
	Api := &structs.API{}
	Api.Code = 1
	proc := r.PostFormValue("proc")
	sublink := r.PostFormValue("sublink")
	if proc != "" {
		procn, err := strconv.Atoi(proc)
		if err != nil {
			maxProcces = 10
		}
		maxProcces = procn
		if maxProcces > 30 {
			maxProcces = 30
		}
	}
	if url == "" || end_url == "" {
		Api.Msg = "url或end_url缺失"
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	if tags == "" {
		tags = "h1,p"
	}
	url = strings.ReplaceAll(url, "｛", "{")
	url = strings.ReplaceAll(url, "｝", "}")
	end_url = strings.ReplaceAll(end_url, "｛", "{")
	end_url = strings.ReplaceAll(end_url, "｝", "}")

	tags = strings.ReplaceAll(tags, "，", ",")
	tags_arr := strings.Split(tags, ",")

	p, s, i, j, err := util.UrlRange(url, end_url)

	if err != nil {
		fmt.Println("book error 1:", err.Error())
		Api.Msg = "book error 1:" + err.Error()
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	map_res := &sync.Map{}
	url_info, err := neturl.Parse(url)
	if err != nil {
		fmt.Println("url error 1:", err.Error())
		Api.Msg = "url error 1:" + err.Error()
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	//开启100个并发进程
	chFinish := make(chan int, maxProcces)
	timeout := time.Duration(downTimeOut) * time.Second
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	if sublink == "" {
		sublink = "_"
	}
	//当前章节链接
	now_chap := 0
	//如果超过当前章节便休眠

	sleep_time := 0
	go func() {
		link_res := make([]string, 0)
		for t := i; t <= j; t++ {
			curl := p + strconv.Itoa(t) + s
			link_res = append(link_res, curl)
		}
		link_his := make([]string, len(link_res))
		copy(link_his, link_res)
		for t := i; t <= j; t++ {
			for {
				if t-now_chap < 2*maxProcces {
					sleep_time = 0
					break
				} else {
					time.Sleep(2 * time.Second)
					sleep_time += 2
					if sleep_time > 60 {
						map_res.Store(now_chap+1, strconv.Itoa(now_chap+1)+"超时")
						sleep_time = 0
						break
					}
				}
			}
			select {
			case <-ctx.Done():
				// 在等待期间上下文被取消，退出外层循环
				link_his = make([]string, 0)
				link_res = make([]string, 0)
				return
			default:
				// 继续等待
			}
			t := t
			go func() {
				curl := p + strconv.Itoa(t) + s

				tt, err := util.BookUrlContent(curl, ua, tags_arr, subpage, sublink, url_info, "", "", &link_his, ctx)
				if err != nil {
					Api.Msg = err.Error()
					api, _ := json.Marshal(Api)
					util.ApiWrite(&w, api)
					map_res.Store(t, "cyberErrorExit")
					return
				}
				fmt.Println(t, "已完成")
				//fmt.Println(matches)

				map_res.Store(t, tt)

				<-chFinish
			}()
			chFinish <- 1
		}
	}()

	if name == "" {
		name = util.RandStringRunes(10)
	}
	dp := string(os.PathSeparator)
	root_path := os.Getenv("ROOT_PATH") + dp + "web" + dp
	filename := "output" + dp + name + ".txt"
	out_path := root_path + filename
	util.FilePutContent(out_path, util.GetDate()+"\r\n"+end_url+"\r\n", false)
	for t := i; t <= j; t += 1 {
		var str interface{}
		for {
			var ok bool
			str, ok = map_res.LoadAndDelete(t)
			if !ok {
				time.Sleep(1 * time.Second)
				continue
			} else {
				break
			}
		}
		//退出了
		if strings.Contains(str.(string), "cyberErrorExit") {
			return
		}
		now_chap = t
		fmt.Println("写入章节:", t)
		util.FilePutContent(out_path, str.(string), true)
	}
	cancel()
	Api.Code = 0
	Api.Data = &map[string]string{
		"root_path": root_path,
		"file_path": filename,
		"data_path": os.Getenv("data_path"),
	}
	api, _ := json.Marshal(Api)
	util.ApiWrite(&w, api)
}

// url下载
func MenuDownLoad(w http.ResponseWriter, r *http.Request) {
	util.ApiHeader(&w)
	name := r.PostFormValue("name")
	url_post := r.PostFormValue("url")
	sort := r.PostFormValue("sort")
	tags := r.PostFormValue("tags")
	subpage := r.PostFormValue("subpage")
	submenu := r.PostFormValue("submenu")
	prefix := r.PostFormValue("prefix")
	prefix = strings.Trim(prefix, " ")

	sublink := r.PostFormValue("sublink")
	proc := r.PostFormValue("proc")
	start := r.PostFormValue("start")
	end := r.PostFormValue("end")
	ua := r.PostFormValue("ua")
	if proc != "" {
		procn, err := strconv.Atoi(proc)
		if err != nil {
			maxProcces = 10
		}
		maxProcces = procn
		if maxProcces > 30 {
			maxProcces = 30
		}
	}
	start_num := 0
	end_num := 0

	var err error
	if start != "" {
		start_num, err = strconv.Atoi(start)
		if err != nil {
			start_num = 0
		}
	}
	if end != "" {
		end_num, err = strconv.Atoi(end)
		if err != nil {
			end_num = 0
		}
	}

	fmt.Println(url_post, tags, subpage)
	Api := &structs.API{}
	Api.Code = 1
	if url_post == "" {
		Api.Msg = "url缺失"
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	if tags == "" {
		tags = "h1,p"
	}
	url_info, err := neturl.Parse(url_post)
	if err != nil {
		fmt.Println("url error 1:", err.Error())
		Api.Msg = "url error 1:" + err.Error()
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	tags = strings.ReplaceAll(tags, "，", ",")
	tags_arr := strings.Split(tags, ",")
	timeout := time.Duration(downTimeOut) * time.Second
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	html, err := util.BookUrlContent(url_post, ua, []string{"body"}, submenu, sublink, url_info, url_post, "html", nil, ctx)
	if err != nil {
		fmt.Println("book error 1:", err.Error())
		Api.Msg = "book error 1:" + err.Error()
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	//fmt.Println(html)
	//p, s, i, j, err := util.UrlRange(url, end_url)

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
	if err != nil {
		fmt.Println("无法解析HTML文档:", err)
		Api.Msg = "无法解析HTML文档"
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	link_arr := make([]string, 0)
	link_res := make([]string, 0)
	link_prefix := ""
	if prefix != "" {
		link_prefix = prefix
	}
	doc.Find("a").Each(func(i int, s *goquery.Selection) {
		// 获取<a>标签的href属性
		href, _ := s.Attr("href")
		if strings.Contains(href, "javascript:") {
			return
		}
		if strings.Contains(url_post, href) {
			fmt.Println("skip 235", href)
			return
		}
		if !strings.Contains(href, link_prefix) {
			fmt.Println("skip 243", href)
			return
		}
		href = util.BookFullUrl(href, url_info, url_post)
		//相同跳过
		if util.UrlCompare(url_post, href) {
			fmt.Println("skip 248", href)
			return
		}
		nhref, err := neturl.QueryUnescape(href)
		if err != nil {
			fmt.Println("book urldecode 304", err.Error())
			nhref = href
		}
		href = nhref
		fmt.Println("push", href)
		link_arr = append(link_arr, href)
	})
	//fmt.Println(link_arr)
	//return
	if sort == "true" || sort == "1" {
		link_arr = util.SliceReverse(link_arr)
	}

	if prefix == "" {
		link_prefix = util.UrlPrefix(link_arr)
	} else {
		link_prefix = prefix
	}

	if link_prefix == "" {
		Api.Msg = "无法解析目录"
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	fmt.Println("link_prefix", link_prefix)

	elMap := util.SliceCountElements(link_arr)
	ll := 0
	//过滤链接
	for _, url := range link_arr {
		if !strings.Contains(url, link_prefix) {
			continue
		}
		//
		if elMap[url] > 1 {
			fmt.Println("later", url, elMap[url])
			elMap[url] -= 1
			continue
		}
		//过界了
		ll += 1
		if ll-1 < start_num && start_num > 0 {
			continue
		}

		if ll-1 > end_num && end_num > 0 {
			continue
		}
		//fmt.Println(url, elMap[url])
		link_res = append(link_res, url)
	}
	//fmt.Println(elMap)
	//return
	root_path := ""
	filename := ""
	msg := ""
	if sublink == "" {
		sublink = "_"
	}
	if maxProcces == 1 {
		root_path, filename, msg = util.BookOneDownload(maxProcces, link_res, url_post, name, tags_arr, subpage, sublink, ua)
	} else {
		root_path, filename, msg = util.BookMutiDownload(maxProcces, link_res, url_post, name, tags_arr, subpage, sublink, ua)
	}

	Api.Code = 0
	Api.Msg = msg
	Api.Data = &map[string]string{
		"root_path": root_path,
		"file_path": filename,
		"data_path": os.Getenv("data_path"),
	}
	api, _ := json.Marshal(Api)
	util.ApiWrite(&w, api)
}

// 查询所有书名
func BookList(w http.ResponseWriter, r *http.Request) {
	util.ApiHeader(&w)
	name := r.PostFormValue("name")
	books := strings.Split(name, ";")
	Api := &structs.API{}
	if name == "" {
		Api.Code = 1
		api, _ := json.Marshal(Api)
		util.ApiWrite(&w, api)
		return
	}
	data := make(map[string]map[string]string)
	dp := string(os.PathSeparator)
	root_path := os.Getenv("ROOT_PATH") + dp + "web" + dp + "output" + dp
	for _, book := range books {
		data[book] = make(map[string]string)
		fpath := root_path + dp + book + ".txt"
		info, err := os.Stat(fpath)
		if err != nil {
			data[book]["url"] = ""
			continue
		}
		data[book]["url"] = "/output/" + dp + book + ".txt"
		data[book]["size"] = strconv.Itoa(int(info.Size() / 1000))
		data[book]["time"] = util.FormatTime(info.ModTime())
	}
	Api.Data = data
	api, _ := json.Marshal(Api)
	util.ApiWrite(&w, api)
}
